package com.rex.editor.view;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.http.SslError;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.webkit.GeolocationPermissions;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import androidx.annotation.ColorInt;

import com.base.common.utils.DensityUtil;
import com.base.common.utils.LogUtil;
import com.base.common.utils.UIUtils;
import com.rex.editor.R;
import com.rex.editor.common.CommonJs;
import com.rex.editor.common.Utils;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;


/**
 * Copyright (C) 2017 Wasabeef
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

public class RichEditor extends WebView implements CommonJs, GestureDetector.OnGestureListener {


    public enum Type {
        BOLD,
        ITALIC,
        SUBSCRIPT,
        SUPERSCRIPT,
        STRIKETHROUGH,
        UNDERLINE,
        H1,
        H2,
        H3,
        H4,
        H5,
        H6,
        ORDEREDLIST,
        UNORDEREDLIST,
        JUSTIFYCENTER,
        JUSTIFYFULL,
        JUSTUFYLEFT,
        JUSTIFYRIGHT
    }

    //光标当前所在的位置
    public interface OnCursorMoveListener {
        void onCursorMove(float x, float y);
    }

    interface OnTextChangeListener {

        void onTextChange(String text);
    }

    public interface OnDecorationStateListener {

        void onStateChangeListener(String text, List<Type> types);
    }

    public interface AfterInitialLoadListener {

        void onAfterInitialLoad(boolean isReady);
    }

    private static final String HTML = "file:///android_asset/editor.html";
    private static final String CALLBACK_SCHEME = "re-callback://";
    private static final String STATE_SCHEME = "re-state://";
    private boolean isReady = false;
    private String mContents = "";
    private OnCursorMoveListener onCursorMoveListener;
    private OnTextChangeListener mTextChangeListener;
    private OnDecorationStateListener mDecorationStateListener;
    private AfterInitialLoadListener mLoadListener;
    public OnClickImageTagListener mOnClickImageTagListener;

    private boolean isInputFinish = false;//是否刚完成输入
    //文字的点击事件
    private OnClickTextTagListener onClickTextTagListener;

    public interface OnClickImageTagListener {
        void onClick(String url);
    }

    public interface OnClickTextTagListener {
        void onClick(Context context, String type, String arguments);
    }

    public void setOnClickImageTagListener(OnClickImageTagListener onClickImageTagListener) {
        this.mOnClickImageTagListener = onClickImageTagListener;
    }

    public void setOnCursorMoveListener(OnCursorMoveListener onCursorMoveListener) {
        this.onCursorMoveListener = onCursorMoveListener;
    }

    public void setOnClickTextTagListener(OnClickTextTagListener onClickTextTagListener) {
        this.onClickTextTagListener = onClickTextTagListener;
    }

    public RichEditor(Context context) {
        this(context, null);
    }

    public RichEditor(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.webViewStyle);
    }

    @SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"})
    public RichEditor(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs, defStyleAttr);
    }

    @SuppressLint("SetJavaScriptEnabled")
    public void initView(Context context, AttributeSet attrs, int defStyleAttr) {
        setVerticalScrollBarEnabled(false);
        setHorizontalScrollBarEnabled(false);

        WebSettings setting = getSettings();
        setting.setDefaultTextEncodingName("utf-8");
        setting.setJavaScriptEnabled(true);

        setting.setDomStorageEnabled(true);
        setting.setBlockNetworkImage(false);
//        setting.setCacheMode(WebSettings.LOAD_CACHE_ONLY);//设置缓存模式
        setting.setAllowFileAccess(true);// 允许访问文件
//        setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //Webview不允许一个安全的站点（https）去加载非安全的站点内容（http）,比如，https网页内容的图片是http链接。强烈建议App使用这种模式，因为这样更安全。
            setting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
            //在这种模式下，WebView是可以在一个安全的站点（Https）里加载非安全的站点内容（Http）,这是WebView最不安全的操作模式，尽可能地不要使用这种模式。
//            setting.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
        }
//        setting.setLoadsImagesAutomatically(true);////设置自动加载图片
        setWebChromeClient(new WebChromeClient());
        setWebViewClient(createWebviewClient());
        addJavascriptInterface(new JavascriptInterfaceImageOnclick(), "imageOnclick");
        addJavascriptInterface(new JavascriptInterfaceTextOnclick(), "textOnclick");
        loadUrl(HTML);
        applyAttributes(context, attrs);

    }

    protected EditorWebViewClient createWebviewClient() {
        return new EditorWebViewClient();
    }

    void setOnTextChangeListener(OnTextChangeListener listener) {
        mTextChangeListener = listener;
    }

    public void setOnDecorationChangeListener(OnDecorationStateListener listener) {
        mDecorationStateListener = listener;
    }

    public void setOnInitialLoadListener(AfterInitialLoadListener listener) {
        mLoadListener = listener;
    }


    private void callback(String text) {

        String mContentNews = text.replaceFirst(CALLBACK_SCHEME, "");
        //删除
        if (mContentNews.length() < mContents.length()) {
            char[] charNews = mContentNews.toCharArray();
            char[] charContents = mContents.toCharArray();
            int indexDellStart = 0;//删除的位置
            for (int i = 0; i < charNews.length; i++) {
                if (charNews[i] != charContents[i]) {
                    indexDellStart = i;
                    break;
                }
            }
            if (indexDellStart == 0) indexDellStart = charNews.length;

            //检查删除的位置是不是在<label ></label> 中间
            String str = mContentNews.substring(0, indexDellStart);
            int s1 = str.lastIndexOf("</label>");//从删除位置往前查找
            int s2 = str.lastIndexOf("<label ");

            int s3 = mContentNews.indexOf("</label>", indexDellStart);//从删除的位置往后查找
            int s4 = mContentNews.indexOf("<label ", indexDellStart);

            int indexStart = 0, indexEnd = 0;
            if (s2 > s1 && (s3 < s4 || (s4 == -1 && s3 != -1))) {
                //删除的位置在标签中间，删除整个标签的内容
                indexStart = s2;
                indexEnd = s3 + 8;
            } else {
                //删除的位置在标签之外
                if (s2 < s1 && s2 > -1) {
                    if (str.endsWith("</label>")) {
                        indexStart = s2;
                        indexEnd = indexDellStart;
                    }
                }
            }
            if (indexStart == indexEnd) {
                mContents = mContentNews;
            } else {
                String delStr = mContentNews.substring(indexStart, indexEnd);
                deleteLabel(delStr);
                mContents = mContentNews.substring(0, indexStart) + mContentNews.substring(indexEnd);
//                setHtml(mContents, indexStart);
//                focusEditor();
            }

        } else {
            isInputFinish = true;
            mContents = mContentNews;
        }
        if (mTextChangeListener != null) {
            mTextChangeListener.onTextChange(mContents);
        }
    }

    private void stateCheck(String text) {
        String state = text.replaceFirst(STATE_SCHEME, "").toUpperCase(Locale.ENGLISH);
        List<Type> types = new ArrayList<>();
        for (Type type : Type.values()) {
            if (TextUtils.indexOf(state, type.name()) != -1) {
                types.add(type);
            }
        }

        if (mDecorationStateListener != null) {
            mDecorationStateListener.onStateChangeListener(state, types);
        }
    }

    private void applyAttributes(Context context, AttributeSet attrs) {
        final int[] attrsArray = new int[]{
                android.R.attr.gravity
        };
        TypedArray ta = context.obtainStyledAttributes(attrs, attrsArray);

        int gravity = ta.getInt(0, NO_ID);
        switch (gravity) {
            case Gravity.LEFT:
                exec("javascript:RE.setTextAlign(\"left\")");
                break;
            case Gravity.RIGHT:
                exec("javascript:RE.setTextAlign(\"right\")");
                break;
            case Gravity.TOP:
                exec("javascript:RE.setVerticalAlign(\"top\")");
                break;
            case Gravity.BOTTOM:
                exec("javascript:RE.setVerticalAlign(\"bottom\")");
                break;
            case Gravity.CENTER_VERTICAL:
                exec("javascript:RE.setVerticalAlign(\"middle\")");
                break;
            case Gravity.CENTER_HORIZONTAL:
                exec("javascript:RE.setTextAlign(\"center\")");
                break;
            case Gravity.CENTER:
                exec("javascript:RE.setVerticalAlign(\"middle\")");
                exec("javascript:RE.setTextAlign(\"center\")");
                break;
            default:
                break;
        }

        ta.recycle();
    }

    public void deleteLabel(String deleteStr) {
        if (UIUtils.isEmpty(deleteStr)) return;
        Document doc = Jsoup.parse(deleteStr);
        Elements elementsImg = doc.select("label[name]");
        for (Element element : elementsImg) {
            String names = element.attr("name");
            String data = element.attr("data");

            exec("javascript:RE.deleteLabel('" + names + "','" + data + "');");
        }
    }

    public void setHtml(String contents, int... pos) {
        if (contents == null) {
            contents = "";
        }
        try {
            exec("javascript:RE.setHtml('" + URLEncoder.encode(contents, "UTF-8") + "');");
            if (pos.length > 0) {
                exec("javascript:RE.setCaretPosition('" + pos[0] + "');");
            }
        } catch (UnsupportedEncodingException e) {
            // No handling
        }
        mContents = contents;
    }


    public String getHtml() {
        return mContents;
    }

    public void setEditorFontColor(int color) {
        String hex = convertHexColorString(color);
        exec("javascript:RE.setBaseTextColor('" + hex + "');");
    }

    public void setEditorFontSize(int px) {
        exec("javascript:RE.setBaseFontSize('" + px + "px');");
    }

    @Override
    public void setPadding(int left, int top, int right, int bottom) {
        super.setPadding(left, top, right, bottom);
        exec("javascript:RE.setPadding('" + left + "px', '" + top + "px', '" + right + "px', '" + bottom
                + "px');");
    }

    @Override
    public void setPaddingRelative(int start, int top, int end, int bottom) {
        // still not support RTL.
        setPadding(start, top, end, bottom);
    }

    public void setEditorBackgroundColor(int color) {
        setBackgroundColor(color);
    }

    @Override
    public void setBackgroundColor(int color) {
        super.setBackgroundColor(color);
    }

    @Override
    public void setBackgroundResource(int resid) {
        Bitmap bitmap = Utils.decodeResource(getContext(), resid);
        String base64 = Utils.toBase64(bitmap);
        bitmap.recycle();

        exec("javascript:RE.setBackgroundImage('url(data:image/png;base64," + base64 + ")');");
    }

    @Override
    public void setBackground(Drawable background) {
        Bitmap bitmap = Utils.toBitmap(background);
        String base64 = Utils.toBase64(bitmap);
        bitmap.recycle();

        exec("javascript:RE.setBackgroundImage('url(data:image/png;base64," + base64 + ")');");
    }

    public void setBackground(String url) {
        exec("javascript:RE.setBackgroundImage('url(" + url + ")');");
    }

    public void setEditorWidth(int px) {
        exec("javascript:RE.setWidth('" + px + "px');");
    }

    public void setEditorHeight(int px) {
        exec("javascript:RE.setHeight('" + px + "px');");
    }

    public void setPlaceholder(String placeholder) {
        exec("javascript:RE.setPlaceholder('" + placeholder + "');");
    }


    public void setInputEnabled(Boolean inputEnabled) {
        exec("javascript:RE.setInputEnabled(" + inputEnabled + ")");
    }

    public void loadCSS(String cssFile) {
        String jsCSSImport = "(function() {" +
                "    var head  = document.getElementsByTagName(\"head\")[0];" +
                "    var link  = document.createElement(\"link\");" +
                "    link.rel  = \"stylesheet\";" +
                "    link.type = \"text/css\";" +
                "    link.href = \"" + cssFile + "\";" +
                "    link.media = \"all\";" +
                "    head.appendChild(link);" +
                "}) ();";
        exec("javascript:" + jsCSSImport + "");
    }

    public void undo() {
        exec("javascript:RE.undo();");
    }

    public void redo() {
        exec("javascript:RE.redo();");
    }

    public void setBold() {
        exec("javascript:RE.setBold();");
    }

    public void setItalic() {
        exec("javascript:RE.setItalic();");
    }

    /**
     * 设置下标 氧气O2
     */
    public void setSubscript() {
        exec("javascript:RE.setSubscript();");
    }

    /**
     * 设置右上角  x的平方 x2
     */
    public void setSuperscript() {
        exec("javascript:RE.setSuperscript();");
    }

    public void setStrikeThrough() {
        exec("javascript:RE.setStrikeThrough();");
    }

    public void setUnderline() {
        exec("javascript:RE.setUnderline();");
    }

    public void setTextColor(@ColorInt int color) {
        exec("javascript:RE.prepareInsert();");

        String hex = convertHexColorString(color);
        exec("javascript:RE.setTextColor('" + hex + "');");
    }

    public void setTextBackgroundColor(int color) {
        exec("javascript:RE.prepareInsert();");

        String hex = convertHexColorString(color);
        exec("javascript:RE.setTextBackgroundColor('" + hex + "');");
    }

    public void setFontSize(int fontSize) {
        if (fontSize > 7 || fontSize < 1) {
            Log.e("RichEditor", "Font size should have a value between 1-7");
        }
        exec("javascript:RE.setFontSize('" + fontSize + "');");
    }

    public void removeFormat() {
        exec("javascript:RE.removeFormat();");
    }

    public void setHeading(int heading) {
        exec("javascript:RE.setHeading('" + heading + "');");
    }

    public void setIndent() {
        exec("javascript:RE.setIndent();");
    }

    public void setOutdent() {
        exec("javascript:RE.setOutdent();");
    }

    public void setAlignLeft() {
        exec("javascript:RE.setJustifyLeft();");
    }

    public void setAlignCenter() {
        exec("javascript:RE.setJustifyCenter();");
    }

    public void setAlignRight() {
        exec("javascript:RE.setJustifyRight();");
    }

    public void setBlockquote() {
        exec("javascript:RE.setBlockquote();");
    }

    public void setBullets() {
        exec("javascript:RE.setBullets();");
    }

    public void setNumbers() {
        exec("javascript:RE.setNumbers();");
    }


    public void insertImage(String url, String alt, String style) {
        exec("javascript:RE.prepareInsert();");
        exec("javascript:RE.insertImage('" + url + "', '" + alt + "', '" + style + "');");
    }

    public void insertLabel(String type, String arguments, String content, String color) {
        exec("javascript:RE.prepareInsert();");
        exec("javascript:RE.insertLabel('" + type + "', '" + arguments + "', '" + content + "', '" + color + "');");
    }

    /**
     * 插入链接
     *
     * @param href  连接
     * @param title 显示的内容
     */
    public void insertLink(String href, String title) {
        exec("javascript:RE.prepareInsert();");
        exec("javascript:RE.insertLink('" + href + "', '" + title + "');");
    }

    public void insertTodo() {
        exec("javascript:RE.prepareInsert();");
        exec("javascript:RE.setTodo('" + Utils.getCurrentTime() + "');");
    }

    public void focusEditor() {
        requestFocus();
        exec("javascript:RE.focus();");
    }

    public void clearFocusEditor() {
        exec("javascript:RE.blurFocus();");
    }

    private String convertHexColorString(int color) {
        return String.format("#%06X", (0xFFFFFF & color));
    }

    protected void exec(final String trigger) {
        if (isReady) {
            load(trigger);
        } else {
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    exec(trigger);
                }
            }, 100);
        }
    }

    private void load(String trigger) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            evaluateJavascript(trigger, null);
        } else {
            loadUrl(trigger);
        }
    }

    protected class EditorWebViewClient extends WebViewClient {
        @Override
        public void onPageFinished(WebView view, String url) {
            isReady = url.equalsIgnoreCase(HTML);
            if (mLoadListener != null) {
                mLoadListener.onAfterInitialLoad(isReady);
            }
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            String decode;
            try {
                decode = URLDecoder.decode(url, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                // No handling
                return false;
            }
            if (url.startsWith("intent") || url.startsWith("youku")) {
                return true;
            }
            if (TextUtils.indexOf(url, CALLBACK_SCHEME) == 0) {
                callback(decode);
                return true;
            } else if (TextUtils.indexOf(url, STATE_SCHEME) == 0) {
                stateCheck(decode);
                return true;
            }


            return super.shouldOverrideUrlLoading(view, url);
        }

        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//            super.onReceivedSslError(view, handler, error);
            handler.proceed();// 接受所有网站的证书
        }

        @Override
        public void onLoadResource(WebView view, String url) {
            super.onLoadResource(view, url);
            LogUtil.d("webview_url", url);
        }
    }

    //图片的点击事件
    public class JavascriptInterfaceImageOnclick {

        @android.webkit.JavascriptInterface
        public void openImage(String imgUrl) {
            if (mOnClickImageTagListener != null && !TextUtils.isEmpty(imgUrl)) {
                mOnClickImageTagListener.onClick(imgUrl);
            }
        }
    }

    //文字的点击事件
    public class JavascriptInterfaceTextOnclick {

        @android.webkit.JavascriptInterface
        public void onTextClick(String type, String arguments) {
            if (onClickTextTagListener != null && !TextUtils.isEmpty(type) && !TextUtils.isEmpty(arguments)) {
                onClickTextTagListener.onClick(getContext(), type, arguments);
            }
        }
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        left = l;
        top = t;
        if (isInputFinish) {
            isInputFinish = false;
            if (isPopup) {
                if (onCursorMoveListener != null) {
                    int bottom = 0;
                    if (getLayoutParams() instanceof MarginLayoutParams) {
                        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) getLayoutParams();
                        bottom = marginLayoutParams.bottomMargin;
                    }
                    onCursorMoveListener.onCursorMove(l, b - bottom);
                }
            }
        }

    }

    //添加手势
    GestureDetector gd = new GestureDetector(getContext(), this);

    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    private float cursorY;
    private int left, top;

    private boolean isPopup = false;

    @Override
    public boolean onSingleTapUp(final MotionEvent e) {
        cursorY = e.getY();
        if (getHeight() - cursorY < 60) {
            isPopup = true;
        } else {
            isPopup = false;
        }
        if (onCursorMoveListener != null) {
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    onCursorMoveListener.onCursorMove(e.getRawX(), e.getRawY() + 60);
                }
            }, 200);
        }
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        gd.onTouchEvent(event);//把onTouchEvent托管给手势类
        return super.onTouchEvent(event);
    }

}
